package is.xdumper.xml;

import is.xdumper.type.Dump;
import is.xdumper.type.Field;
import is.xdumper.type.Entity;
import is.bean.entity.department.Department;
import is.bean.entity.employee.Employee;
import is.bean.entity.region.Region;
import java.util.HashSet;
import nullutil.NullUtil;
import org.xml.sax.Attributes;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;

/**
 * Handles SAX events, writes XML data to dump
 *
 * @author aspr0
 */
public class XdumperXmlHandler extends DefaultHandler
{
    private Entity entityCurrent;
    private Field fieldCurrent;
    private Department departmentCurrent;
    private Employee employeeCurrent;
    private Region regionCurrent;
    private Dump dump = new Dump();
    private HashSet<Department> departmentTable = new HashSet<Department>();
    private HashSet<Employee> employeeTable = new HashSet<Employee>();
    private HashSet<Region> regionTable = new HashSet<Region>();

    public Dump getDump()
    {
        return dump;
    }

    @Override
    public void startElement(String namespaceURI, String localName, String qName, Attributes atts)
    {
        if (qName.equals("dump"))
        {
            for (int attNum = 0; attNum < atts.getLength(); attNum++)
            {
                String attName = atts.getQName(attNum);
                String attValue = atts.getValue(attName);

                if (attName.equals("dumpReferences"))
                {
                    dump.setDumpReferences(Boolean.parseBoolean(attValue));
                }
                else if (attName.equals("majorEntity"))
                {
                    if (attValue.equals("DEPARTMENT"))
                    {
                        dump.setMajorEntity(Entity.DEPARTMENT);
                    }
                    else if (attValue.equals("EMPLOYEE"))
                    {
                        dump.setMajorEntity(Entity.EMPLOYEE);
                    }
                    else if (attValue.equals("REGION"))
                    {
                        dump.setMajorEntity(entityCurrent = Entity.REGION);
                    }
                }
            }
        }
        /* Tables */
        else if (qName.equals("department"))
        {
            entityCurrent = Entity.DEPARTMENT;
        }
        else if (qName.equals("employee"))
        {
            entityCurrent = Entity.EMPLOYEE;
        }
        else if (qName.equals("region"))
        {
            entityCurrent = Entity.REGION;
        }
        /* Row starts */
        else if (qName.equals("row"))
        {
            switch (entityCurrent)
            {
                case DEPARTMENT:
                    departmentCurrent = new Department();
                    break;

                case EMPLOYEE:
                    employeeCurrent = new Employee();
                    break;

                case REGION:
                    regionCurrent = new Region();
                    break;
            }
        }
        /* Fields */
        else if (qName.equals("id"))
        {
            fieldCurrent = Field.ID;
        }
        else if (qName.equals("parentId"))
        {
            fieldCurrent = Field.PARENT_ID;
        }
        else if (qName.equals("regionId"))
        {
            fieldCurrent = Field.REGION_ID;
        }
        else if (qName.equals("bossId"))
        {
            fieldCurrent = Field.BOSS_ID;
        }
        else if (qName.equals("departmentId"))
        {
            fieldCurrent = Field.DEPARTMENT_ID;
        }
        else if (qName.equals("name"))
        {
            fieldCurrent = Field.NAME;
        }
        else if (qName.equals("firstName"))
        {
            fieldCurrent = Field.FIRST_NAME;
        }
        else if (qName.equals("lastName"))
        {
            fieldCurrent = Field.LAST_NAME;
        }
        else if (qName.equals("middleName"))
        {
            fieldCurrent = Field.MIDDLE_NAME;
        }
    }

    @Override
    public void endElement(String uri, String localName, String qName)
    {
        if (qName.equals("row"))
        {
            switch (entityCurrent)
            {
                case DEPARTMENT:
                    if (NullUtil.isPositiveInteger(departmentCurrent.getId()))
                    {
                        departmentTable.add(departmentCurrent);
                    }
                    break;

                case EMPLOYEE:
                    if (NullUtil.isPositiveInteger(employeeCurrent.getId()))
                    {
                        employeeTable.add(employeeCurrent);
                    }
                    break;

                case REGION:
                    if (NullUtil.isPositiveInteger(regionCurrent.getId()))
                    {
                        regionTable.add(regionCurrent);
                    }
                    break;
            }
        }
    }

    @Override
    public void characters(char[] ch, int start, int length)
    {
        String xmlString = new String(ch, start, length);

        int xmlInt = 0;
        if (fieldCurrent.isId())
        {
            try
            {
                xmlInt = Integer.parseInt(xmlString);
            }
            catch (NumberFormatException e)
            {
                e.printStackTrace();        // todo add values check to schema
            }
        }

        /* XSD schema check pass must guarantee existence of every field */
        switch (entityCurrent)
        {
            case DEPARTMENT:
                switch (fieldCurrent)
                {
                    case ID:
                        departmentCurrent.setId(xmlInt);
                        break;
                    case NAME:
                        departmentCurrent.setName(xmlString);
                        break;
                    case PARENT_ID:
                        departmentCurrent.setParentId(xmlInt);
                        break;
                    case REGION_ID:
                        departmentCurrent.setRegionId(xmlInt);
                        break;
                    case BOSS_ID:
                        departmentCurrent.setBossId(xmlInt);
                        break;
                }
                break;

            case EMPLOYEE:
                switch (fieldCurrent)
                {
                    case ID:
                        employeeCurrent.setId(xmlInt);
                        break;
                    case LAST_NAME:
                        employeeCurrent.setLastName(xmlString);
                        break;
                    case FIRST_NAME:
                        employeeCurrent.setFirstName(xmlString);
                        break;
                    case MIDDLE_NAME:
                        employeeCurrent.setMiddleName(xmlString);
                        break;
                    case DEPARTMENT_ID:
                        employeeCurrent.setDepartmentId(xmlInt);
                        break;
                }
                break;

            case REGION:
                switch (fieldCurrent)
                {
                    case ID:
                        regionCurrent.setId(xmlInt);
                        break;
                    case NAME:
                        regionCurrent.setName(xmlString);
                        break;
                    case PARENT_ID:
                        regionCurrent.setParentId(xmlInt);
                        break;
                }
                break;
        }
    }

    @Override
    public void endDocument()
    {
        dump.setDepartmentTable(departmentTable);
        dump.setEmployeeTable(employeeTable);
        dump.setRegionTable(regionTable);
    }

    @Override
    public void error(SAXParseException e) throws SAXParseException
    {
        throw e;
    }

    @Override
    public void warning(SAXParseException e) throws SAXParseException
    {
        throw e;
    }

    @Override
    public void fatalError(SAXParseException e) throws SAXParseException
    {
        throw e;
    }
}
